﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tomahawk.Runtime.Canvas.Sprite;
using System.Xml.Serialization;
using Tomahawk.Runtime.Logic.UI.Components;
using Microsoft.Xna.Framework;

namespace Tomahawk.Runtime.Logic.UI
{
    /// <summary>
    /// This controls shows a button that can be focused and clicked by the user
    /// </summary>
    public class XUIButton : XUIPlaceableControl
    {

        #region "Serializable Properties"

        [XmlElement("UnselectedImage")]
        public ImageComponent ImageUnselected;
        
        [XmlElement("SelectedImage")]
        public ImageComponent ImageSelected;
        
        [XmlElement("PressedImage")]
        public ImageComponent ImagePressed;

        [XmlElement("Text")]
        public TextComponent TextComponent;

        [XmlAttribute("animate")]
        public bool AnimateOnSelect = true;

        [XmlAttribute("animduration")]
        public float AnimationDuration = 0.75f;

        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        public XUIButton()
        {
            this.TextComponent = new TextComponent();
            this.ImageUnselected.ResourcePool = Tomahawk.Runtime.Resources.ResourceManager.ResourcePool.GameState;
            this.ImageSelected.ResourcePool = Tomahawk.Runtime.Resources.ResourceManager.ResourcePool.GameState;
            this.ImagePressed.ResourcePool = Tomahawk.Runtime.Resources.ResourceManager.ResourcePool.GameState;
        }

        /// <summary>
        /// Control Initialization
        /// </summary>
        protected override void OnInitialize()
        {
            base.OnInitialize();

            //
            // image components
            //

            this.ImagePressed.Initialize(this.ParentUIScene.CanvasLayer);
            this.ImageSelected.Initialize(this.ParentUIScene.CanvasLayer);
            this.ImageUnselected.Initialize(this.ParentUIScene.CanvasLayer);

            this.TextComponent.Initialize(this.ParentUIScene.CanvasLayer);

            //show / hide the right sprite
            this.changeState(ControlStates.Unselected);

            //copy position and size to sprites
            this.passPositionToSprites();
        }

        protected override void OnPreBeginPlay()
        {
            base.OnPreBeginPlay();

            // Size store.
            this.TextComponent.TextSize = new Vector2(Position.Width, Position.Height);

            // Set up the text (may be word wrap).
            TextComponent.SetUpText();
        }

        #region "State Management"

        /// <summary>
        /// Available states for a button
        /// </summary>
        protected enum ControlStates
        {
            Unselected,
            Selected,
            Pressed
        }

        protected ControlStates currentState = ControlStates.Unselected;

        //color animation variables
        XStyleManager.FontColor aColor;
        XStyleManager.FontColor bColor;
        private float colorAnimTimeLeft = 0.0f;

        /// <summary>
        /// Change control state
        /// </summary>
        protected void changeState(ControlStates newState)
        {
            //assign
            this.currentState = newState;

            if (newState == ControlStates.Selected)
            {
                //reset color states
                this.aColor = this.TextComponent.TextStyle.SelectedColor;
                this.bColor = this.TextComponent.TextStyle.SelectedAltColor;
                this.colorAnimTimeLeft = this.AnimationDuration;

                this.UpdateRequired = true;
            }

            //set visibility
            this.setSpriteVisibility();
            this.setTextColor();
        }


        #endregion

        #region "Visibility"

        /// <summary>
        /// Make the appropiate sprite visible, and hide the rest
        /// </summary>
        private void setSpriteVisibility()
        {
            switch (this.currentState)
            {
                case ControlStates.Pressed:
                    this.ImagePressed.SetVisibility(true);
                    this.ImageSelected.SetVisibility(false);
                    this.ImageUnselected.SetVisibility(false);
                    break;
                case ControlStates.Selected:
                    this.ImagePressed.SetVisibility(false);
                    this.ImageSelected.SetVisibility(true);
                    this.ImageUnselected.SetVisibility(false);
                    break;
                case ControlStates.Unselected:
                    this.ImagePressed.SetVisibility(false);
                    this.ImageSelected.SetVisibility(false);
                    this.ImageUnselected.SetVisibility(true);
                    break;
            }
        }

        /// <summary>
        /// Sets the appropiate text color, upon the current state
        /// </summary>
        private void setTextColor()
        {
            switch (this.currentState)
            {
                case ControlStates.Pressed: 
                    this.TextComponent.SetFontColor(ref this.TextComponent.TextStyle.PressedColor);
                    break;
                case ControlStates.Unselected: 
                    this.TextComponent.SetFontColor(ref this.TextComponent.TextStyle.DefaultColor);
                    break;
                case ControlStates.Selected:
                    this.TextComponent.SetFontColor(ref this.TextComponent.TextStyle.SelectedColor);
                    break;
            }
        }

        /// <summary>
        /// Controls visibility changes
        /// </summary>
        protected override void OnChangeVisibility(bool newValue)
        {
            base.OnChangeVisibility(newValue);

            if (!newValue)
            {
                this.ImagePressed.SetVisibility(false);
                this.ImageSelected.SetVisibility(false);
                this.ImageUnselected.SetVisibility(false);
                this.TextComponent.SetVisibility(false);
            }
            else
            {
                this.setSpriteVisibility();
                this.TextComponent.SetVisibility(true);
                this.setTextColor();
            }
        }

        #endregion

        #region "Rendering"
        /// <summary>
        /// Prepares the control for rendering
        /// </summary>
        protected override void OnPrepareSpritesForRendering()
        {
            base.OnPrepareSpritesForRendering();
            this.passPositionToSprites();
        }
        #endregion

        #region "Positioning"

        /// <summary>
        /// Handle a position change
        /// </summary>
        protected override void OnPositionChange()
        {
            base.OnPositionChange();
        }

        /// <summary>
        /// Copy the control position to the sprites
        /// </summary>
        private void passPositionToSprites()
        {
            this.ImagePressed.SetPosition(ref this.renderRect);
            this.ImageSelected.SetPosition(ref this.renderRect);
            this.ImageUnselected.SetPosition(ref this.renderRect);

            this.ImagePressed.SetRotation(this.renderRot, ref this.renderOrigin);
            this.ImageSelected.SetRotation(this.renderRot, ref this.renderOrigin);
            this.ImageUnselected.SetRotation(this.renderRot, ref this.renderOrigin);

            this.TextComponent.SetPosition(ref this.renderRect);
            this.TextComponent.SetFontScale(0.5f * (this.renderScale.X + this.renderScale.Y));
            this.TextComponent.SetRotation(this.renderRot, ref this.renderOrigin);
        }

        #endregion

        #region "Focus handling"

        protected override void OnFocus()
        {
            base.OnFocus();
            this.changeState(ControlStates.Selected);
        }

        protected override void OnUnfocus()
        {
            base.OnUnfocus();
            this.changeState(ControlStates.Unselected);
        }

        #endregion

        #region "Button Pressed Event"

        float pressedLeftTime = 0;

        /// <summary>
        /// Handle this event to respond to press on this button control
        /// </summary>
        public event ControlUserActivation ButtonPressed;

        /// <summary>
        /// Handle user activation of the control
        /// </summary>
        protected override void OnUserActivate()
        {
            base.OnUserActivate();

            //set the pressed state
            this.changeState(ControlStates.Pressed);

            //enable ticking
            this.UpdateRequired = true;

            //assign some pressed time
            this.pressedLeftTime = 0.1f;
            
            //call the event
            if (ButtonPressed != null) ButtonPressed(this);
        }

        /// <summary>
        /// Update the pressed animation on the button
        /// </summary>
        private void handlePressedTime(float elapsedTime)
        {
            //count back until no more pressed time left
            pressedLeftTime -= elapsedTime;
            if (pressedLeftTime < 0)
            {
                pressedLeftTime = 0;
                //change state back to selected/unselected
                if (this.Focused)
                    this.changeState(ControlStates.Selected);
                else
                    this.changeState(ControlStates.Unselected);
                //no more ticking required
                //this.UpdateRequired = false;
            }
        }

        #endregion

        /// <summary>
        /// Control update. 
        /// </summary>
        protected override void OnUpdate(float elapsedTime)
        {
            base.OnUpdate(elapsedTime);

            //pressed animation
            if (this.currentState == ControlStates.Pressed)
                handlePressedTime(elapsedTime);

            if (this.currentState == ControlStates.Selected)
                animateSelectionColorChange(elapsedTime);
        }

        XStyleManager.FontColor _interpColor = new XStyleManager.FontColor();

        private void animateSelectionColorChange(float elapsedTime)
        {
            if (this.AnimateOnSelect)
            {
                this.colorAnimTimeLeft -= elapsedTime;
                if (this.colorAnimTimeLeft < 0.0f)
                {
                    //exchange colors
                    XStyleManager.FontColor temp = this.aColor;
                    this.aColor = this.bColor;
                    this.bColor = temp;

                    //reset anim time
                    this.colorAnimTimeLeft = this.AnimationDuration;
                }
                else
                {
                    //set an interpolation between the two colors
                    float interpValue = this.colorAnimTimeLeft / this.AnimationDuration;
                    _interpColor.R = (byte)(bColor.R + ((aColor.R - bColor.R) * interpValue));
                    _interpColor.G = (byte)(bColor.G + ((aColor.G - bColor.G) * interpValue));
                    _interpColor.B = (byte)(bColor.B + ((aColor.B - bColor.B) * interpValue));
                    _interpColor.A = (byte)(bColor.A + ((aColor.A - bColor.A) * interpValue));
                    this.TextComponent.SetFontColor(ref _interpColor);
                }
            }
        }

        /// <summary>
        /// Destruction handling
        /// </summary>
        public override void OnBeforeDestroy()
        {
            base.OnBeforeDestroy();

            this.ImageSelected.Destroy();
            this.ImagePressed.Destroy();
            this.ImageUnselected.Destroy();
            this.TextComponent.Destroy();
        }

        #region "Readyness"

        /// <summary>
        /// Returns true when all the resource dependant objects are ready
        /// </summary>
        protected override bool checkResourcesReady()
        {
            return base.checkResourcesReady() &&
                this.ImagePressed.IsReady() &&
                this.ImageSelected.IsReady() &&
                this.ImageUnselected.IsReady() &&
                this.TextComponent.IsReady();
        }

        #endregion

        /// <summary>
        /// Gets or sets the alpha of the image and text components
        /// </summary>
        protected override void OnAlphaChange(byte newAlpha)
        {
            base.OnAlphaChange(newAlpha);
            this.ImageSelected.Alpha = newAlpha;
            this.ImagePressed.Alpha = newAlpha;
            this.ImageUnselected.Alpha = newAlpha;
            this.TextComponent.Alpha = newAlpha;
        }

    }

}
